/*
 * jQuery JSON Plugin
 * version: 1.0 (2008-04-17)
 *
 * This document is licensed as free software under the terms of the
 * MIT License: http://www.opensource.org/licenses/mit-license.php
 *
 * Brantley Harris technically wrote this plugin, but it is based somewhat
 * on the JSON.org website's http://www.json.org/json2.js, which proclaims:
 * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
 * I uphold.  I really just cleaned it up.
 *
 * It is also based heavily on MochiKit's serializeJSON, which is 
 * copywrited 2005 by Bob Ippolito.
 */

    (function ($) {
        function toIntegersAtLease(n)
        // Format integers to have at least two digits.
        {
            return n < 10 ? '0' + n : n;
        }

        Date.prototype.toJSON = function (date)
            // Yes, it polutes the Date namespace, but we'll allow it here, as
            // it's damned usefull.
        {
            return this.getUTCFullYear() + '-' +
                toIntegersAtLease(this.getUTCMonth()) + '-' +
                toIntegersAtLease(this.getUTCDate());
        };

        var escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
        var meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"': '\\"',
            '\\': '\\\\'
        };

        $.quoteString = function (string)
            // Places quotes around a string, inteligently.
            // If the string contains no control characters, no quote characters, and no
            // backslash characters, then we can safely slap some quotes around it.
            // Otherwise we must also replace the offending characters with safe escape
            // sequences.
        {
            if (escapeable.test(string)) {
                return '"' + string.replace(escapeable, function (a) {
                    var c = meta[a];
                    if (typeof c === 'string') {
                        return c;
                    }
                    c = a.charCodeAt();
                    return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
                }) + '"';
            }
            return '"' + string + '"';
        };

        $.toJSON = function (o) {
            var type = typeof(o);

            if (type == "undefined")
                return "undefined";
            else if (type == "number" || type == "boolean")
                return o + "";
            else if (o === null)
                return "null";

            // Is it a string?
            if (type == "string") {
                return $.quoteString(o);
            }

            // Does it have a .toJSON function?
            if (type == "object" && typeof o.toJSON == "function")
                return o.toJSON();

            // Is it an array?
            if (type != "function" && typeof(o.length) == "number") {
                var ret = [];
                for (var i = 0; i < o.length; i++) {
                    ret.push($.toJSON(o[i]));
                }
                return "[" + ret.join(",") + "]";
            }

            // If it's a function, we have to warn somebody!
            if (type == "function") {
                throw new TypeError("Unable to convert object of type 'function' to json.");
            }

            // It's probably an object, then.
            var ret = [];
            for (var k in o) {
                var name;
                type = typeof(k);

                if (type == "number")
                    name = '"' + k + '"';
                else if (type == "string")
                    name = $.quoteString(k);
                else
                    continue;  //skip non-string or number keys

                var val = $.toJSON(o[k]);
                if (typeof(val) != "string") {
                    // skip non-serializable values
                    continue;
                }

                ret.push(name + ":" + val);
            }
            return "{" + ret.join(",") + "}";
        };

        $.compactJSON = function (o) {
            return $.toJSON(o, true);
        };

        $.evalJSON = function (src)
            // Evals JSON that we know to be safe.
        {
            return eval("(" + src + ")");
        };

        $.secureEvalJSON = function (src)
            // Evals JSON in a way that is *more* secure.
        {
            var filtered = src;
            filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
            filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
            filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');

            if (/^[\],:{}\s]*$/.test(filtered))
                return eval("(" + src + ")");
            else
                throw new SyntaxError("Error parsing JSON, source is not valid.");
        };
    })(jQuery);

